<?php

namespace EasySwoole;

use \EasySwoole\Core\AbstractInterface\EventInterface;
use \EasySwoole\Core\Http\Request;
use \EasySwoole\Core\Http\Response;
use \EasySwoole\Core\Swoole\EventHelper;
use \EasySwoole\Core\Swoole\EventRegister;
use \EasySwoole\Core\Swoole\Process\ProcessManager;
use \EasySwoole\Core\Swoole\ServerManager;
use App\Model\User\UserModel;
use App\Server\Process\Inotify;
use App\Server\Process\OnlineNum;
use App\Server\PoolServer\MysqlPool2;
use App\Server\PoolServer\RedisPool;
use App\Server\Sock\Parser\WebSock;
use App\Utility\Common;
use App\WsController\WsAbstract;
use EasySwoole\Core\Component\Di;
use EasySwoole\Core\Component\Pool\PoolManager;

/**
 * 全局事件定义文件11
 * Class EasySwooleEvent
 *
 * @package EasySwoole
 */
class EasySwooleEvent implements EventInterface {
	
	/**
	 * 框架初始化事件
	 * 在Swoole没有启动之前 会先执行这里的代码
	 */
	public static function frameInitialize(): void {
		date_default_timezone_set ( 'Asia/Shanghai' );
	}
	
	/**
	 * 创建主服务
	 * 除了主服务之外还可以在这里创建额外的端口监听
	 *
	 * @param ServerManager $server        	
	 * @param EventRegister $register        	
	 */
	public static function mainServerCreate(ServerManager $server, EventRegister $register): void {
		$conf = Config::getInstance ()->getConf ( 'MYSQL' );
		
		Di::getInstance ()->set ( 'MYSQL', \MysqliDb::class, Array (
				'host' => $conf ['HOST'],
				'username' => $conf ['USER'],
				'password' => $conf ['PASSWORD'],
				'db' => $conf ['DB_NAME'],
				'port' => $conf ['PORT'],
				'charset' => 'utf8' 
		) );
		
		// 定时器
		// CronTab::getInstance ()->addRule ( 'test1', '@daily', function () {
		// var_dump ( 'run only once every daty' );
		// } )->addRule ( 'test2', '*/1 * * * *', function () {
		// var_dump ( 'run per min at' . time () );
		// } )->addRule ( 'test3', '*/2 * * * *', function () {
		// var_dump ( 'run per 2min at' . time () );
		// } );
		
		// 数据库协程连接池
		// @see https://www.easyswoole.com/Manual/2.x/Cn/_book/CoroutinePool/mysql_pool.html?h=pool
		// ------------------------------------------------------------------------------------------
		if (version_compare ( phpversion ( 'swoole' ), '2.1.0', '>=' )) {
			
			$mysqlpool = PoolManager::getInstance ()->registerPool ( MysqlPool2::class, 3, 100, PoolManager::TYPE_ONLY_WORKER );
			if ($mysqlpool) {
				(new Common ())->E ( "创建 mysql 连接池 sucess!" );
			} else {
				(new Common ())->E ( "创建 mysql 连接池 error!" );
			}
			$redispool = PoolManager::getInstance ()->registerPool ( RedisPool::class, 3, 100, PoolManager::TYPE_ONLY_WORKER );
			
			if ($redispool) {
				(new Common ())->E ( "创建 redis 连接池 sucess!" );
			} else {
				(new Common ())->E ( "创建 redis 连接池 error!" );
			}
		}
		
		// 普通事件注册 swoole 中的各种事件都可以按这个例子来进行注册
		// @see https://www.easyswoole.com/Manual/2.x/Cn/_book/Core/event_register.html
		// ------------------------------------------------------------------------------------------
		$register->add ( $register::onWorkerStart, function (\swoole_server $server, $workerId) {
			// 为第一个进程添加定时器
			// (new Common())->E("onWorkerStart---------->workerId = {$workerId}");
			if ($workerId == 0) {
				(new UserModel ())->InitOnlineNum ();
			}
		} );
		// EventHelper::registerDefaultOnMessage ( $register, new WebSock () );
		EventHelper::registerDefaultOnMessage ( $register, WebSock::class );
		$register->add ( $register::onOpen, function (\swoole_server $server, $fd, $reactorId) {
			WsAbstract::onOpen ( $server, $fd, $reactorId );
		} );
		
		$register->add ( $register::onClose, function (\swoole_server $server, $fd, $reactorId) {
			WsAbstract::onClose ( $server, $fd, $reactorId );
		} );
		
		$register->add ( $register::onConnect, function (\swoole_server $server, $fd, $reactorId) {
			WsAbstract::onConnect ( $server, $fd, $reactorId );
		} );
		// 天天都在问的服务热重启 单独启动一个进程处理
		// ------------------------------------------------------------------------------------------
		ProcessManager::getInstance ()->addProcess ( 'autoReload', Inotify::class );
		ProcessManager::getInstance ()->addProcess ( 'OnlineNum', OnlineNum::class );
		
		// 自定义WS握手处理 可以实现在握手的时候 鉴定用户身份
		// @see https://wiki.swoole.com/wiki/page/409.html
		// ------------------------------------------------------------------------------------------
		$register->add ( $register::onHandShake, function (\swoole_http_request $request, \swoole_http_response $response) {
			if (isset ( $request->cookie ['shaketoken'] )) {
				$token = $request->cookie ['shaketoken'];
				if ($token == '123') {
					// 如果取得 token 并且验证通过 则进入 ws rfc 规范中约定的验证过程
					if (! isset ( $request->header ['sec-websocket-key'] )) {
						// 需要 Sec-WebSocket-Key 如果没有拒绝握手
						(new Common ())->E ( 'shake fai1 3' );
						
						$response->end ();
						return false;
					}
					if (0 === preg_match ( '#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header ['sec-websocket-key'] ) || 16 !== strlen ( base64_decode ( $request->header ['sec-websocket-key'] ) )) {
						// 不接受握手
						(new Common ())->E ( 'shake fai1 4' );
						$response->end ();
						return false;
					}
					
					$key = base64_encode ( sha1 ( $request->header ['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true ) );
					$headers = array (
							'Upgrade' => 'websocket',
							'Connection' => 'Upgrade',
							'Sec-WebSocket-Accept' => $key,
							'Sec-WebSocket-Version' => '13',
							'KeepAlive' => 'off' 
					);
					foreach ( $headers as $key => $val ) {
						$response->header ( $key, $val );
					}
					// 接受握手 发送验证后的header 还需要101状态码以切换状态
					$response->status ( 101 );
					(new Common ())->E ( 'shake success at fd :' . $request->fd );
					$response->end ();
				} else {
					// 令牌不正确的情况 不接受握手
					(new Common ())->E ( 'shake fail 2' );
					$response->end ();
				}
			} else {
				// 没有携带令牌的情况 不接受握手
				(new Common ())->E ( 'shake fai1 1' );
				$response->end ();
			}
		} );
	}
	public static function onRequest(Request $request, Response $response): void {
		// 每个请求进来都先执行这个方法 可以作为权限验证 前置请求记录等
		$response->setCookie ( "shaketoken", "123" );
		// $request->withAttribute ( 'requestTime', microtime ( true ) );
	}
	public static function afterAction(Request $request, Response $response): void {
		// 每个请求结束后都执行这个方法 可以作为后置日志等
		// $start = $request->getAttribute ( 'requestTime' );
		// $spend = round ( microtime ( true ) - $start, 3 );
		// Logger::getInstance ()->console ( "request :{$request->getUri()->getPath()} take {$spend}" );
	}
}